﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace Spinnerets.Network
{
    public class NetworkAddress : IComparable<NetworkAddress>
    {
        IPAddress _coreAddress = IPAddress.Loopback;
        LinkedList<ulong> _octets = new LinkedList<ulong>();

        /// <summary>
        /// Gets the core <see cref="IPAddress"/> associated with the current instance.
        /// </summary>
        /// <value>The core address.</value>
        public IPAddress CoreAddress
        {
            get { return _coreAddress; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IpAddress"/> class.
        /// </summary>
        /// <param name="address">The address.</param>
        public NetworkAddress(IPAddress address)
        {
            _coreAddress = address;
            RefreshOctets();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="NetworkAddress"/> class.
        /// </summary>
        /// <param name="addressString">The address string.</param>
        public NetworkAddress(string addressString)
        {
            IPAddress.TryParse(addressString, out _coreAddress);
            RefreshOctets();
        }

        /// <summary>
        /// Refreshes the octet list from the core <see cref="IPAddress"/>.
        /// </summary>
        void RefreshOctets()
        {
            _coreAddress.GetAddressBytes().ToList().ForEach(
                delegate(byte octet)
                {
                    ulong octetNumber = 0;
                    ulong.TryParse(Convert.ToString(octet), out octetNumber);
                    _octets.AddLast(octetNumber);
                }
            );
        }

        /// <summary>
        /// Implements the operator ++ by incrementing the network address by 1 octet.
        /// </summary>
        /// <param name="address">The address.</param>
        /// <returns>The result of the operator.</returns>
        public static NetworkAddress operator ++(NetworkAddress address)
        {
            IncrementNode(address._octets.Last);
            address._coreAddress = new IPAddress(address._octets.Select(o => Convert.ToByte(o)).ToArray());
            return address;
        }

        /// <summary>
        /// Increments the specified octet linked list node.
        /// </summary>
        /// <param name="octetNode">The octet node.</param>
        private static void IncrementNode(LinkedListNode<ulong> octetNode)
        {
            if (octetNode.Value > 254)
            {
                if (octetNode.Previous == null) return;

                octetNode.Value = 0;
                IncrementNode(octetNode.Previous);
            }
            else octetNode.Value++;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return _coreAddress.ToString();
        }

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings:
        /// Value
        /// Meaning
        /// Less than zero
        /// This object is less than the <paramref name="other"/> parameter.
        /// Zero
        /// This object is equal to <paramref name="other"/>.
        /// Greater than zero
        /// This object is greater than <paramref name="other"/>.
        /// </returns>
        public int CompareTo(NetworkAddress other)
        {
            int compareValue = 0;

            LinkedListNode<ulong> thisOctet = _octets.First, otherOctet = other._octets.First;
            while (compareValue == 0 && thisOctet != null && otherOctet != null)
            {
                compareValue = thisOctet.Value.CompareTo(otherOctet.Value);

                thisOctet = thisOctet.Next;
                otherOctet = otherOctet.Next;
            }

            return compareValue;
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator <=(NetworkAddress left, NetworkAddress right)
        {
            return (left.CompareTo(right) < 1);
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="left">The left.</param>
        /// <param name="right">The right.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator >=(NetworkAddress left, NetworkAddress right)
        {
            return (left.CompareTo(right) > 0);
        }
    }
}
